package com.duoduo.common.util.postProcessor;


import com.duoduo.common.api.constants.Constant;
import com.duoduo.common.api.help.ArrayInnerUtil;
import com.duoduo.common.api.help.MapInnerUtil;
import com.duoduo.common.api.help.PropertiesInnerUtil;
import com.duoduo.common.api.help.SpringContextInnerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.Ordered;
import org.springframework.core.env.*;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**这么写的目的就是让SpringContextInnerUtil.setApplicationContext在所有bean初始化之前先执行
 * @author shiguang
 *
 */
// 解决非springboot项目的配置信息加载问题，通过PropertySource注解加载的配置文件可以在解析BeanDefinition前加载到环境中
@org.springframework.context.annotation.PropertySource(value = {"classpath:/application.properties"}, ignoreResourceNotFound = true)
public class BootstrapConfigPostProcessor implements BeanFactoryPostProcessor, Ordered, ApplicationContextAware {

	private static final Logger LOGGER = LoggerFactory.getLogger(BootstrapConfigPostProcessor.class);
	private ApplicationContext applicationContext;

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		medusaUtilInit();
		initOtherConfig();
	}

	private void medusaUtilInit(){
		SpringContextInnerUtil.setApplicationContext(applicationContext);
		PropertySourcesPlaceholderConfigurer[] configurers = SpringContextInnerUtil.getBeans(PropertySourcesPlaceholderConfigurer.class);
		if(ArrayInnerUtil.isEmpty(configurers)){
			LOGGER.error("BootstrapConfigPostProcessor error: cant not get configurers");
			return;
		}
		PropertySources[] propertySourceses = new PropertySources[configurers.length];
		for (int i=0; i<configurers.length; i++) {
			propertySourceses[i] = configurers[i].getAppliedPropertySources();
		}

		String beanoptType = getStringValue(propertySourceses, "beanopt.type", Constant.BEANOPT_TYPE_DEFAULT);
		String jsonType = getStringValue(propertySourceses, "json.type", Constant.JSON_TYPE_DEFAULT);
		String excelType = getStringValue(propertySourceses, "excel.type", Constant.EXCEL_TYPE_DEFAULT);
		String workIdType = getStringValue(propertySourceses, "workIdType.type", Constant.WORKID_TYPE_DEFAULT);
		String serializationType = getStringValue(propertySourceses, "serialization.type", Constant.SERIALIZATION_TYPE_DEFAULT);
		String lockType = getStringValue(propertySourceses, "lock.type", Constant.LOCK_TYPE_DEFAULT);
		String leaderType = getStringValue(propertySourceses, "leader.type", Constant.LEADER_TYPE_DEFAULT);
		String cacheType = getStringValue(propertySourceses, "cache.type", Constant.CACHE_TYPE_DEFAULT);
		String redisImpl = getStringValue(propertySourceses, "redis.impl", Constant.REDIS_IMPL_DEFAULT);
		String zookeeperImpl = getStringValue(propertySourceses, "zookeeper.impl", Constant.ZOOKEEPER_IMPL_DEFAULT);
		String mqType = getStringValue(propertySourceses, "mq.type", Constant.MQ_TYPE_DEFAULT);
		String rabbitImpl = getStringValue(propertySourceses, "rabbit.impl", Constant.RABBIT_IMPL_DEFAULT);
		String kafkaImpl = getStringValue(propertySourceses, "kafka.impl", Constant.KAFKA_IMPL_DEFAULT);
		String rocketImpl = getStringValue(propertySourceses, "rocket.impl", Constant.ROCKET_IMPL_DEFAULT);
		String traceScheduleType = getStringValue(propertySourceses, "traceSchedule.type", Constant.TRACE_SCHEDULE_TYPE_DEFAULT);
		String consistencyRetryImpl = getStringValue(propertySourceses, "consistency.retry.impl", Constant.CONSISTENCY_RETRY_IMPL_DEFAULT);
		Boolean consistencyRetryEnable = getBooleanValue(propertySourceses, "consistency.retry.enabled", Constant.CONSISTENCY_RETRY_ENABLED_DEFAULT);
		String delayType = getStringValue(propertySourceses, "delaymng.type", Constant.DELAY_MNGTYPE_DEFAULT);
		String delayJvmType = getStringValue(propertySourceses, "delaymng.jvm.type", Constant.DELAY_JVM_MNGTYPE_DEFAULT);
		String esImpl = getStringValue(propertySourceses, "es.impl", Constant.ES_IMPL_DEFAULT);
		String mongoImpl = getStringValue(propertySourceses, "mongo.impl", Constant.MONGO_IMPL_DEFAULT);
		String tracerReporterLogType = getStringValue(propertySourceses, "tracer.reporter.type", Constant.TRACER_REPORTER_LOG_TYPE_DEFAULT);
		String workflowImpl = getStringValue(propertySourceses, "workflow.impl", Constant.WORKFLOW_IMPL_DEFAULT);
		String configCenterImpl = getStringValue(propertySourceses, "configCenter.impl", Constant.CONFIGCENTER_IMPL_DEFAULT);
		String taskImpl = getStringValue(propertySourceses, "task.impl", Constant.TASK_IMPL_DEFAULT);
		String encryptType = getStringValue(propertySourceses, "encrypt.type", Constant.ENCRYPT_TYPE_DEFAULT);
		String restType = getStringValue(propertySourceses, "rest.type", Constant.REST_TYPE_DEFAULT);
		String nacosServerAddr = getStringValue(propertySourceses, "spring.cloud.nacos.discovery.server-addr", Constant.EMPTY_STRING);
		String nacosNamespace = getStringValue(propertySourceses, "spring.cloud.nacos.discovery.namespace", Constant.EMPTY_STRING);
		
		SpringContextInnerUtil.putConfigIfAbsent(Constant.BEANOPT_TYPE, beanoptType);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.JSON_TYPE, jsonType);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.EXCEL_TYPE, excelType);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.WORKID_TYPE, workIdType);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.SERIALIZATION_TYPE, serializationType);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.LOCK_TYPE, lockType); // 分布式锁的具体实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.LEADER_TYPE, leaderType); // 选主的具体实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.CACHE_TYPE, cacheType); // 缓存的具体实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.REDIS_IMPL, redisImpl);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.ZOOKEEPER_IMPL, zookeeperImpl);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.MQ_TYPE, mqType); // 消息中间件
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RABBIT_IMPL, rabbitImpl);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.KAFKA_IMPL, kafkaImpl);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.ROCKET_IMPL, rocketImpl);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.TRACE_SCHEDULE_TYPE, traceScheduleType); // 链路编排默认实现是mq
		SpringContextInnerUtil.putConfigIfAbsent(Constant.CONSISTENCY_RETRY_ENABLED, consistencyRetryEnable);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.DELAY_MNGTYPE, delayType); // 分布式延迟任务的具体实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.DELAY_JVM_MNGTYPE, delayJvmType); // jvm延迟任务的具体实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.ES_IMPL, esImpl); // es默认是es6实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.MONGO_IMPL, mongoImpl); // mongo默认是spring实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.TRACER_REPORTER_LOG_TYPE, tracerReporterLogType); // 链路监控的reporter实现类型
		SpringContextInnerUtil.putConfigIfAbsent(Constant.WORKFLOW_IMPL, workflowImpl); // 工作流默认是activiti实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.CONFIGCENTER_IMPL, configCenterImpl); // 配置中心默认apollo实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.TASK_IMPL, taskImpl); // 定时任务默认sia实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.ENCRYPT_TYPE, encryptType); // 对称加解密aes实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.CONSISTENCY_RETRY_IMPL, consistencyRetryImpl); // 传输保障实现
		SpringContextInnerUtil.putConfigIfAbsent(Constant.REST_TYPE, restType); // http请求类型
		SpringContextInnerUtil.putConfigIfAbsent(Constant.NACOSSERVERADDR, nacosServerAddr);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.NACOSNAMESPACE, nacosNamespace);
		
		String trIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_TRACESCHEDULE_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_TRACESCHEDULE_INTERVALSECOND, trIntervalSecond);
		String delayIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_DELAY_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_DELAY_INTERVALSECOND, delayIntervalSecond);
		String httpIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_HTTPCLIENT_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_HTTPCLIENT_INTERVALSECOND, httpIntervalSecond);
		String duIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_DUBBOCLIENT_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_DUBBOCLIENT_INTERVALSECOND, duIntervalSecond);
		String meIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_METHOD_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_METHOD_INTERVALSECOND, meIntervalSecond);
		String mqIntervalSecond = getStringValue(propertySourceses, Constant.RETRY_MQ_INTERVALSECOND, Constant.EMPTY_STRING);
		SpringContextInnerUtil.putConfigIfAbsent(Constant.RETRY_MQ_INTERVALSECOND, mqIntervalSecond);
	}

	private String getStringValue(PropertySources[] propertySourceses, String propertyName, String defaultValue) {
		for (PropertySources propertySources : propertySourceses) {
			Iterator<PropertySource<?>> it = propertySources.iterator();
			while(it.hasNext()){
				PropertySource<?> propertySource = it.next();
				Object propertyValue = propertySource.getProperty(propertyName);
				if(propertyValue != null){
					if(propertyValue.getClass().isAssignableFrom(String.class)){
						return (String)propertyValue;
					}else{
						LOGGER.error("BootstrapConfigPostProcessor getStringValue, propertyName: " + propertyName + " is not String.");
					}
				}
			}
		}
		return defaultValue;
	}

	private Boolean getBooleanValue(PropertySources[] propertySourceses, String propertyName, Boolean defaultValue) {
		for (PropertySources propertySources : propertySourceses) {
			Iterator<PropertySource<?>> it = propertySources.iterator();
			while(it.hasNext()){
				PropertySource<?> propertySource = it.next();
				Object propertyValue = propertySource.getProperty(propertyName);
				if(propertyValue != null){
					// 类型是Boolean或者是true或false的字符串，都认为是boolean
					if(propertyValue.getClass().isAssignableFrom(Boolean.class)){
						return (Boolean)propertyValue;
					}else if(propertyValue.getClass().isAssignableFrom(String.class)){
						String propertyValueStr = (String)propertyValue;
						if(Constant.TRUE.equals(propertyValueStr.toLowerCase()) || Constant.FALSE.equals(propertyValueStr.toLowerCase())){
							return Boolean.valueOf(propertyValueStr);
						}else{
							LOGGER.error("BootstrapConfigPostProcessor getBooleanValue, propertyName: " + propertyName + " is not Boolean Str.");
						}
					}else{
						LOGGER.error("BootstrapConfigPostProcessor getBooleanValue, propertyName: " + propertyName + " is not Boolean.");
					}
				}
			}
		}
		return defaultValue;
	}

	/**
	 * 对一些额外信息进行兼容配置
	 */
	private void initOtherConfig() {
		String taskZookeeperServer = PropertiesInnerUtil.getString(Constant.ZK_PROP_LOCATION, Constant.ZK_PROP_TASK_SERVER);
		Map<String, Object> map = new HashMap<>();
		if(taskZookeeperServer != null){
			map.put(Constant.SIA_TASK_ZOOKEEPER_HOST, taskZookeeperServer);
		}

		if(MapInnerUtil.isNotEmpty(map)){
			ConfigurableEnvironment environment = (ConfigurableEnvironment)applicationContext.getEnvironment();
			CompositePropertySource composite = new CompositePropertySource(Constant.OTHERBOOTSTRAPPROPERTYSOURCES);
			MapPropertySource configPropertySource = new MapPropertySource(Constant.OTHER_CONFIG, map);
			composite.addPropertySource(configPropertySource);
			environment.getPropertySources().addLast(composite);
		}
	}

	@Override
	public int getOrder() {
		return 50;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

}
